import os
import sys
import zipfile
import shutil
import subprocess
import time
import threading
from pathlib import Path
from tkinter import messagebox
import customtkinter as ctk
from queue import Queue, Empty
from config.config import APP_ROOT, TEMP_ZIP_FILE, TEMP_UNZIP_DIR, CURRENT_EXE
from utils.utils import (
    logger, verify_sha256, is_64bit_os, is_64bit_file,
    is_admin, kill_process_by_name, clean_old_mei_tempdirs
)
import requests

def extract_zip(zip_path: Path, extract_dir: Path) -> list[Path]:
    if extract_dir.exists():
        shutil.rmtree(extract_dir)
    extract_dir.mkdir(exist_ok=True)
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(extract_dir)
        extracted_files = [extract_dir / f for f in zip_ref.namelist()]
    logger.info(f"[解压] 成功解压 {len(extracted_files)} 个文件到 {extract_dir}")
    return extracted_files

def find_required_files(extract_dir: Path):
    main_exe = extract_dir / "DataCollector.exe"
    updater_exe = extract_dir / "updater.exe"
    version_txt = extract_dir / "version.txt"
    return main_exe, updater_exe, version_txt

def exit_and_launch_updater(main_exe: Path, updater_exe: Path, version_txt: Path, expected_sha256: str):
    logger.info("[退出流程] 主程序准备主动退出...")
    kill_process_by_name(CURRENT_EXE.name)
    for thread in threading.enumerate():
        if thread != threading.main_thread():
            try:
                thread.join(timeout=2.0)
            except:
                pass
    clean_old_mei_tempdirs()
    time.sleep(2)
    kill_process_by_name(CURRENT_EXE.name)
    time.sleep(1)

    try:
        if getattr(sys, 'frozen', False):  # 实际应判断主程序是否打包
            updater_executable = APP_ROOT / "updater.exe"
            cmd = [
                str(updater_executable),
                str(main_exe),
                str(updater_exe),
                str(version_txt),
                expected_sha256
            ]
        else:
            updater_script = APP_ROOT / "updater.py"
            cmd = [
                sys.executable,
                str(updater_script),
                str(main_exe),
                str(updater_exe),
                str(version_txt),
                expected_sha256
            ]
        logger.info(f"[退出流程] 启动 updater 命令: {cmd}")
        subprocess.Popen(
            cmd,
            shell=True,
            cwd=str(APP_ROOT),
            close_fds=True,
            creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
        )
    except Exception as e:
        logger.error(f"[退出流程] 启动 updater 失败: {e}", exc_info=True)
        messagebox.showerror("更新错误", f"无法启动更新器：{e}")
    os._exit(0)


def perform_update_flow(
                        current_ver: str,
                        remote_ver: str,
                        download_url: str, changelog: str,
                        expected_sha256: str, remote_arch: str) -> bool:
    # 架构兼容性校验
    is_64 = is_64bit_os()
    if is_64 and remote_arch not in ("x64", "64"):
        messagebox.showerror("架构不兼容", f"当前系统为64位，但更新包为{remote_arch}位，无法更新！")
        return False
    if not is_64 and remote_arch not in ("x86", "32"):
        messagebox.showerror("架构不兼容", f"当前系统为32位，但更新包为{remote_arch}位，无法更新！")
        return False

    if not is_admin():
        messagebox.showerror("权限不足", "更新需要管理员权限。请右键程序，选择“以管理员身份运行”后再试。")
        return False

    updater_win = ctk.CTk()
    updater_win.title("正在更新...")
    updater_win.geometry("500x200")
    updater_win.resizable(False, False)
    updater_win.attributes("-topmost", True)
    status_label = ctk.CTkLabel(updater_win, text="准备中...", font=("Microsoft YaHei", 12))
    status_label.pack(pady=5)
    progress_bar = ctk.CTkProgressBar(updater_win, mode="determinate")
    progress_bar.set(0.0)
    progress_bar.pack(pady=5, padx=20, fill="x")
    speed_label = ctk.CTkLabel(updater_win, text="速度: -- KB/s", font=("Microsoft YaHei", 10), text_color="gray")
    speed_label.pack()
    cancel_event = threading.Event()
    result_queue = Queue()
    update_queue = Queue()

    def background_update_flow():
        try:
            if TEMP_ZIP_FILE.exists():
                TEMP_ZIP_FILE.unlink()
            if TEMP_UNZIP_DIR.exists():
                shutil.rmtree(TEMP_UNZIP_DIR)

            update_queue.put(("status", "正在连接服务器..."))
            resp = requests.get(download_url, stream=True, timeout=30)
            resp.raise_for_status()
            total = int(resp.headers.get('content-length', 0))
            downloaded = 0
            last_time = time.time()
            last_downloaded = 0
            with open(TEMP_ZIP_FILE, 'wb') as f:
                for chunk in resp.iter_content(chunk_size=8192):
                    if cancel_event.is_set():
                        if TEMP_ZIP_FILE.exists():
                            TEMP_ZIP_FILE.unlink()
                        raise Exception("用户取消更新")
                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        now = time.time()
                        elapsed = now - last_time
                        if elapsed > 0.5:
                            speed_kb = (downloaded - last_downloaded) / elapsed / 1024
                            speed_text = f"速度: {speed_kb:.1f} KB/s | 已下载: {downloaded // 1024} KB"
                            update_queue.put(("speed", speed_text))
                            last_time = now
                            last_downloaded = downloaded
                        if total > 0:
                            progress = min(0.7, downloaded / total * 0.7)
                            update_queue.put(("progress", progress))
            update_queue.put(("status", "正在解压更新包..."))
            extract_zip(TEMP_ZIP_FILE, TEMP_UNZIP_DIR)

            main_exe, updater_exe, version_txt = find_required_files(TEMP_UNZIP_DIR)
            if not all([main_exe.exists(), updater_exe.exists(), version_txt.exists()]):
                raise Exception("更新包缺少必需文件：DataCollector.exe、updater.exe 或 version.txt")

            update_queue.put(("status", "正在校验主程序完整性..."))
            if not verify_sha256(main_exe, expected_sha256):
                raise Exception("主程序SHA256校验失败")

            update_queue.put(("status", "正在校验文件架构..."))
            is_64bit = is_64bit_file(main_exe)
            if is_64bit is None:
                raise Exception("无法识别主程序架构")
            if is_64bit_os() and not is_64bit:
                raise Exception("主程序为32位，与64位系统不兼容")
            if not is_64bit_os() and is_64bit:
                raise Exception("主程序为64位，与32位系统不兼容")

            update_queue.put(("status", "校验通过，准备更新..."))
            result_queue.put(("ready_to_update", main_exe, updater_exe, version_txt, expected_sha256))

        except Exception as e:
            error_msg = str(e)
            logger.error(f"[更新] 流程失败: {error_msg}", exc_info=True)
            result_queue.put(("error", error_msg))
        finally:
            if TEMP_ZIP_FILE.exists():
                try:
                    TEMP_ZIP_FILE.unlink()
                except:
                    pass

    thread = threading.Thread(target=background_update_flow, daemon=True)
    thread.start()

    def on_cancel():
        cancel_event.set()
        cancel_button.configure(state="disabled")
        status_label.configure(text="正在取消...")

    cancel_button = ctk.CTkButton(updater_win, text="取消更新", command=on_cancel, fg_color="red", width=100)
    cancel_button.pack(pady=10)

    def process_ui_updates():
        while True:
            try:
                msg_type, payload = update_queue.get_nowait()
                if msg_type == "status":
                    status_label.configure(text=payload)
                elif msg_type == "progress":
                    progress_bar.set(payload)
                elif msg_type == "speed":
                    speed_label.configure(text=payload)
            except Empty:
                break

        if not result_queue.empty():
            msg_type, *payload = result_queue.get()
            if msg_type == "ready_to_update":
                main_exe, updater_exe, version_txt, sha256 = payload
                updater_win.destroy()
                exit_and_launch_updater(main_exe, updater_exe, version_txt, sha256)
                return
            else:
                messagebox.showerror("更新失败", payload[0])
                updater_win.destroy()
                if TEMP_UNZIP_DIR.exists():
                    shutil.rmtree(TEMP_UNZIP_DIR, ignore_errors=True)
                return

        if thread.is_alive():
            updater_win.after(100, process_ui_updates)
        else:
            updater_win.destroy()

    updater_win.after(100, process_ui_updates)
    updater_win.mainloop()